#ifndef __BLOCK_QUEUE_HPP__
#define __BLOCK_QUEUE_HPP__

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h> //原生线程库

// 封装BlockQueue
template <class T>
class BlockQueue
{
    // 为什么私有，因为阻塞队列只提供给生产者放数据和消费者拿数据，至于放没放拿没拿成功，取决于阻塞队列内部控制，
    // 生成者消费者尽管在外面调用入和出接口就行了，放不放进去，拿不拿出来，阻塞队列内部会判断，并对其做出相应的阻塞，
    // 将其加入等待队列， 所以满和空，封装内部就行了，外部不能用。
private:
    // 判断阻塞队列是否为满
    bool IsFull()
    {
        return _block_queue.size() == _cap;
    }
    // 判断阻塞队列是否为空
    bool IsEmpty()
    {
        return _block_queue.empty();
    }

public:
    // 生产者放数据
    void Enqueue(T &in) // 生产者调用的接口
    {
        pthread_mutex_lock(&_mutex); // 上锁，放数据时，别人不能干扰
        // 保持代码的健壮性
        while (IsFull()) // 放数据时，满了，就放不进去，睡眠到等待队列中
        {
            // 需要用while不能简单使用if，因为会导致伪唤醒，导致被唤醒了多个线程，结果生产者与它们竞争锁失败，导致没有数据结果
            // 线程拿数据问题, 现在改循环了，就不怕你一次唤醒多个线程了，只有一个线程能成功，其它被while循环，最终还是等待的
            _productor_wait_num++;                      // 生产者等待的人数量++一下
            pthread_cond_wait(&_product_cond, &_mutex); // 1. pthread_cond_wait调用是: a. 让调用线程等待 b. 自动释放曾经持有的_mutex锁 c. 当条件满足，线程唤醒，pthread_cond_wait要求线性
            // 必须重新竞争_mutex锁，竞争成功，方可返回！！！
            _productor_wait_num--; // 被唤醒，重新上锁，while循环结束
        }
        // 开始进行生产
        _block_queue.push(in);
        // 通知(唤醒)消费者来消费
        if (_consumer_wait_num > 0) // 只有有消费者等待，才会去唤醒
            pthread_cond_signal(&_consum_cond);
        pthread_mutex_unlock(&_mutex); // 解锁
    }

    // Pop   消费者拿数据  通过输出型参数带出，弹出的数据
    void Pop(T *out) // 消费者调用的接口
    {
        pthread_mutex_lock(&_mutex);
        while (IsEmpty())
        {
            _consumer_wait_num++;
            pthread_cond_wait(&_consum_cond, &_mutex);
            _consumer_wait_num--;
        }
        // 消费数据
        *out = _block_queue.front(); // 输出型参数，带出弹出数据
        _block_queue.pop();
        // 通知生产者进行生产
        if (_productor_wait_num > 0) // 有生产者等待才会去唤醒
            pthread_cond_signal(&_product_cond);
        pthread_mutex_unlock(&_mutex);
    }

    // 构造
    BlockQueue(int cap) // 声明一个阻塞队列，它的大小必须给定
        : _cap(cap)
    {
        _productor_wait_num = 0;
        _consumer_wait_num = 0;
        // 初始化锁和条件变量
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_product_cond, nullptr);
        pthread_cond_init(&_consum_cond, nullptr);
    }
    // 析构
    ~BlockQueue()
    {
        // 释放锁和条件变量
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_product_cond);
        pthread_cond_destroy(&_consum_cond);
    }

private:
    std::queue<T> _block_queue;   // 阻塞队列
    int _cap;                     // 阻塞队列容量
    pthread_mutex_t _mutex;       // 保护_block_queue的互斥锁
    pthread_cond_t _product_cond; // 专门给生产者提供的条件变量
    pthread_cond_t _consum_cond;  // 专门给消费者提供的条件变量
    int _productor_wait_num;      // 记录生产者等待队列中生产者的数量
    int _consumer_wait_num;       // 记录消费者等待队列中生产者的数量
};

#endif